#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import base64
import socket
import urllib
import CGIHTTPServer
import BaseHTTPServer
import SocketServer

error_page = 'PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KPG1ldGEgY2hhcnNldD0idXRmLTgiPgo8dGl0bGU+JXM8L3RpdGxlPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5IHsKICAgIGJhY2tncm91bmQtY29sb3I6ICNFQ0VDRUM7CiAgICBmb250LWZhbWlseTogJ09wZW4gU2FucycsIHNhbnMtc2VyaWY7CiAgICBmb250LXNpemU6IDE0cHg7CiAgICBjb2xvcjogIzNjM2MzYzsKICAgIC13ZWJraXQtdXNlci1zZWxlY3Q6IG5vbmU7CiAgICAtbW96LXVzZXItc2VsZWN0OiBub25lOwogICAgLW1zLXVzZXItc2VsZWN0OiBub25lOwp9CgouZGVtbyBwOmZpcnN0LWNoaWxkIHsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgIGZvbnQtZmFtaWx5OiBjdXJzaXZlOwogICAgZm9udC1zaXplOiAxNTBweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgbGluZS1oZWlnaHQ6IDEwMHB4OwogICAgbGV0dGVyLXNwYWNpbmc6IDVweDsKICAgIGNvbG9yOiAjZmZmOwp9CgouZGVtbyBwOmZpcnN0LWNoaWxkIHNwYW4gewogICAgY3Vyc29yOiBwb2ludGVyOwogICAgdGV4dC1zaGFkb3c6IDBweCAwcHggMnB4ICM2ODY4NjgsCiAgICAgICAgMHB4IDFweCAxcHggI2RkZCwKICAgICAgICAwcHggMnB4IDFweCAjZDZkNmQ2LAogICAgICAgIDBweCAzcHggMXB4ICNjY2MsCiAgICAgICAgMHB4IDRweCAxcHggI2M1YzVjNSwKICAgICAgICAwcHggNXB4IDFweCAjYzFjMWMxLAogICAgICAgIDBweCA2cHggMXB4ICNiYmIsCiAgICAgICAgMHB4IDdweCAxcHggIzc3NywKICAgICAgICAwcHggOHB4IDNweCByZ2JhKDEwMCwgMTAwLCAxMDAsIDAuNCksCiAgICAgICAgMHB4IDlweCA1cHggcmdiYSgxMDAsIDEwMCwgMTAwLCAwLjEpLAogICAgICAgIDBweCAxMHB4IDdweCByZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMTUpLAogICAgICAgIDBweCAxMXB4IDlweCByZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMiksCiAgICAgICAgMHB4IDEycHggMTFweCByZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMjUpLAogICAgICAgIDBweCAxM3B4IDE1cHggcmdiYSgxMDAsIDEwMCwgMTAwLCAwLjMpOwogICAgLXdlYmtpdC10cmFuc2l0aW9uOiBhbGwgLjFzIGxpbmVhcjsKICAgIHRyYW5zaXRpb246IGFsbCAuMXMgbGluZWFyOwp9CgouZGVtbyBwOmZpcnN0LWNoaWxkIHNwYW46aG92ZXIgewogICAgdGV4dC1zaGFkb3c6IDBweCAwcHggMnB4ICM2ODY4NjgsCiAgICAgICAgMHB4IDFweCAxcHggI2ZmZiwKICAgICAgICAwcHggMnB4IDFweCAjZmZmLAogICAgICAgIDBweCAzcHggMXB4ICNmZmYsCiAgICAgICAgMHB4IDRweCAxcHggI2ZmZiwKICAgICAgICAwcHggNXB4IDFweCAjZmZmLAogICAgICAgIDBweCA2cHggMXB4ICNmZmYsCiAgICAgICAgMHB4IDdweCAxcHggIzc3NywKICAgICAgICAwcHggOHB4IDNweCAjZmZmLAogICAgICAgIDBweCA5cHggNXB4ICNmZmYsCiAgICAgICAgMHB4IDEwcHggN3B4ICNmZmYsCiAgICAgICAgMHB4IDExcHggOXB4ICNmZmYsCiAgICAgICAgMHB4IDEycHggMTFweCAjZmZmLAogICAgICAgIDBweCAxM3B4IDE1cHggI2ZmZjsKICAgIC13ZWJraXQtdHJhbnNpdGlvbjogYWxsIC4xcyBsaW5lYXI7CiAgICB0cmFuc2l0aW9uOiBhbGwgLjFzIGxpbmVhcjsKfQoKLmRlbW8gcDpub3QoOmZpcnN0LWNoaWxkKSB7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICBjb2xvcjogIzY2NjsKICAgIGZvbnQtZmFtaWx5OiBjdXJzaXZlOwogICAgZm9udC1zaXplOiAyMHB4OwogICAgdGV4dC1zaGFkb3c6IDAgMXB4IDAgI2ZmZjsKICAgIGxldHRlci1zcGFjaW5nOiAxcHg7CiAgICBsaW5lLWhlaWdodDogMmVtOwogICAgbWFyZ2luLXRvcDogLTUwcHg7Cn0KPC9zdHlsZT4KPC9oZWFkPgoKPGJvZHk+CiAgICA8ZGl2IGNsYXNzPSJkZW1vIj4KICAgICAgICA8cD4lczwvcD4KICAgICAgICA8cD4lczwvcD4KICAgIDwvZGl2Pgo8L2JvZHk+CjwvaHRtbD4K'
error_page = base64.b64decode(error_page)
html_wrap = lambda x: '<span>%s</span><span>%s</span><span>%s</span>' % tuple(x)

g_content = {
    403: error_page % ('403', html_wrap('403'), 'Forbidden'),
    404: error_page % ('404', html_wrap('404'), 'Not Found'),
    500: error_page % ('500', html_wrap('500'), 'Internal Server Error')
}


class CGITimeoutHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):

    timeout = 60
    protocol_version = 'HTTP/1.1'

    CGIHTTPServer.CGIHTTPRequestHandler.extensions_map.update({
        '.xhtml': 'application/xhtml+xml'
    })

    def setup(self):
        self.request.settimeout(self.timeout)
        CGIHTTPServer.CGIHTTPRequestHandler.setup(self)

    def is_cgi(self):
        """ is_cgi overridden """
        self.path = urllib.unquote(self.path)
        return CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self)

    def send_response(self, code, message=None):
        """ send_response overridden """
        self.log_request(code)
        if message is None:
            if code in self.responses:
                message = self.responses[code][0]
            else:
                message = ''
        self.wfile.write('%s %d %s\r\n' % (self.protocol_version, code, message))

        self.response_headers = {}
        self.send_header('Server', self.version_string())
        self.send_header('Date', self.date_time_string())
        if code >= 400:
            self.send_header('Connection', 'close')
            self.close_connection = 1
        else:
            self.send_header('Connection', 'keep-alive')
            self.close_connection = 0

    def send_header(self, keyword, value):
        """ send_header overridden """
        self.response_headers[keyword] = value

    def end_headers(self):
        """ end_headers overridden"""
        for key in sorted(self.response_headers.keys()):
            self.wfile.write('%s: %s\r\n' % (key, self.response_headers[key]))
        self.wfile.write('\r\n')

    def send_error(self, code, message=None):
        """ send_error overridden """
        try:
            short, _ = self.responses[code]
        except KeyError:
            short, _ = '???', '???'
        if message is None:
            message = short
        self.send_response(code, message)
        if code in g_content:
            content = g_content[code]
            content_type = 'text/html; charset=utf-8'
        else:
            content = '%d, %s' % (code, message)
            content_type = 'text/plain; charset=utf-8'
        self.send_header('Content-Type', content_type)
        self.send_header('Content-Length', str(len(content)))
        self.end_headers()
        if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
            self.wfile.write(content)

    def address_string(self):
        """ address_string overridden """
        host, port = self.client_address[:2]
        return host

    def version_string(self):
        """ version_string overridden """
        return 'Microsoft-IIS/8.5'

    def list_directory(self, path):
        """ list_directory overridden """
        self.send_error(403)


class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):

    """
    New features w/r to BaseHTTPServer.HTTPServer:
    - serves multiple requests simultaneously
    - catches socket.timeout and socket.error exceptions (raised from RequestHandler)
    """

    request_queue_size = 100
    allow_reuse_address = True
    daemon_threads = True

    def __init__(self, *args):
        BaseHTTPServer.HTTPServer.__init__(self, *args)

    def process_request_thread(self, request, client_address):
        """
        Overrides SocketServer.ThreadingMixIn.process_request_thread
        in order to catch socket.timeout
        """
        try:
            self.finish_request(request, client_address)
            self.close_request(request)
        except socket.timeout:
            sys.stderr.write('Timeout during processing of request from %s\n' % str(client_address))
        except:
            self.close_request(request)


def main():
    if len(sys.argv) == 3:
        ip = sys.argv[1]
        port = int(sys.argv[2])
    elif len(sys.argv) == 1:
        ip = '127.0.0.1'
        port = 8000
    else:
        print '%s <ip> <port>' % os.path.basename(sys.argv[0])
        sys.exit()

    httpd = ThreadedHTTPServer((ip, port), CGITimeoutHTTPRequestHandler)

    print 'Serving HTTP on %s port %d ...' % (ip, port)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print '^C received, shutting down server'


if __name__ == '__main__':
    main()
